package com.open.capacity.finance.service.impl;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.open.capacity.finance.dao.AuditAnalysisDao;
import com.open.capacity.finance.dto.AuditModelBaseResult;
import com.open.capacity.finance.dto.audit_model_34.AuditModelResult;
import com.open.capacity.finance.dto.audit_model_34.AuditModelStep1;
import com.open.capacity.finance.dto.audit_model_34.AuditModelStep2;
import com.open.capacity.finance.dto.audit_model_34.ExcelData;
import com.open.capacity.finance.entity.AuditModel;
import com.open.capacity.finance.entity.AuditModelRule;
import com.open.capacity.finance.service.AbstractAuditMakeResultFactory;
import com.open.capacity.finance.service.AuditModelRuleService;
import com.open.capacity.finance.service.AuditModelService;
import com.open.capacity.finance.util.FileUtil;
import com.open.capacity.nk.dto.NKReceiptDto;
import com.open.capacity.nk.entity.*;
import com.open.capacity.nk.service.*;
import com.open.capacity.utils.StringUtil;
import com.open.capacity.utils.encrypt.Md5Utils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class AuditModelActuator34ServiceImpl<T extends AuditModelBaseResult> extends AbstractAuditMakeResultFactory {

    @Autowired
    private AuditAnalysisDao analysisDao;

    @Autowired
    private AuditModelRuleService ruleService;
    @Autowired
    private AuditModelService modelService;

    @Autowired
    private AaPersonService personService;
    @Autowired
    private AaDepartmentService departmentService;
    @Autowired
    private NkDirectpaymentreceiptService directpaymentreceiptService;
    @Autowired
    private NkContractpaymentreceiptService contractpaymentreceiptService;
    @Autowired
    private NkPrepaidpaymentreceiptService prepaidpaymentreceiptService;
    @Autowired
    private NkApprovalpaymentreceiptService approvalpaymentreceiptService;
    @Autowired
    private NkMaterialpurchasereceiptService materialpurchasereceiptService;


    private static List<String> QUOTANAMES = Arrays.asList("差旅费", "误餐费", "培训费", "赴外培训费", "专家劳务费", "委托业务费");
    private static final String FLOWSTATE = "完成";


    private int TOPN = 3;

    @Override
    protected T analyseResultData(String modelId, Map param) {
        String companyId = param.containsKey("companyId") ? param.get("companyId").toString() : "";
        AuditModelResult result = new AuditModelResult();
        AuditModel model = modelService.findById(modelId);
        Assert.notNull(model, "找不到该模型");
        List<AuditModelRule> rules = ruleService.findByModelId(modelId);

        rules.forEach(t -> {
            if (t.getConfKey().equals("noBmNames")) {
                param.put(t.getConfKey(), t.getConfValue().split(","));
            } else {
                param.put(t.getConfKey(), t.getConfValue());
            }
        });
        result.setModelName(model.getCodeAndName());
        String[] departmentIds = param.containsKey("departmentIds") && StringUtils.isNotEmpty(param.get("departmentIds").toString()) ? param.get("departmentIds").toString().split(",") : null;
        if (departmentIds != null && departmentIds.length > 0) {
            param.put("departmentIds", departmentIds);
        } else {
            param.remove("departmentIds");
        }
        List<AuditModelStep1> list1 = packetBudgetData(param);
        list1.sort(Comparator.comparing(AuditModelStep1::getCompanyId)
                .thenComparing(AuditModelStep1::getKjnd)
                .thenComparing(Comparator.comparing(AuditModelStep1::getAverage).reversed()));
        List<AuditModelStep1> step1List = new ArrayList<>();

        TreeMap<String, List<AuditModelStep1>> collect1 = list1.stream().collect(Collectors.groupingBy(t -> t.getCompanyId(), TreeMap::new, Collectors.toList()));
        for (Map.Entry<String, List<AuditModelStep1>> entry : collect1.entrySet()) {
            if (entry.getValue() != null && entry.getValue().size() > TOPN) {
                step1List.addAll(entry.getValue().subList(0, TOPN));
            }
        }

        step1List.sort(Comparator.comparing(AuditModelStep1::getCompanyId)
                .thenComparing(AuditModelStep1::getKjnd)
                .thenComparing(Comparator.comparing(AuditModelStep1::getAverage).reversed()));

        result.setStep1List(step1List);

        List<AuditModelStep2> list2 = packetMaterialData(param);
        list2.sort(Comparator.comparing(AuditModelStep2::getCompanyId)
                .thenComparing(AuditModelStep2::getKjnd)
                .thenComparing(Comparator.comparing(AuditModelStep2::getAverage).reversed()));
        List<AuditModelStep2> step2List = new ArrayList<>();
        TreeMap<String, List<AuditModelStep2>> collect2 = list2.stream().collect(Collectors.groupingBy(t -> t.getCompanyId(), TreeMap::new, Collectors.toList()));
        for (Map.Entry<String, List<AuditModelStep2>> entry : collect2.entrySet()) {
            if (entry.getValue() != null && entry.getValue().size() > TOPN) {
                step2List.addAll(entry.getValue().subList(0, TOPN));
            }
        }


        step2List.sort(Comparator.comparing(AuditModelStep2::getCompanyId)
                .thenComparing(AuditModelStep2::getKjnd)
                .thenComparing(Comparator.comparing(AuditModelStep2::getAverage).reversed()));

        result.setStep2List(step2List);

        result.setCompanyId(companyId);
        if (CollectionUtils.isEmpty(step1List)) {
            result.setExists(0);
        } else {
            result.setExists(1);

            //持久化本地
            persistence(result);
        }

        return (T) result;
    }

    //报销费用数据
    private List<AuditModelStep1> packetBudgetData(Map params) {
        String[] noBmNames = (String[]) params.get("noBmNames");
        if (params.containsKey("topN")) {
            TOPN = (Integer.parseInt(params.get("topN").toString()));
        }
        params.put("quotaNames", QUOTANAMES);
        params.put("flowstate", FLOWSTATE);
        List<AuditModelStep1> step1List = new ArrayList<>();
        List<NKReceiptDto> receiptDtos = new ArrayList<>();
        List<NkDirectpaymentreceipt> directpaymentreceipts = directpaymentreceiptService.findDepartmentData(params);
        List<NkContractpaymentreceipt> contractpaymentreceipts = contractpaymentreceiptService.findDepartmentData(params);
        List<NkApprovalpaymentreceipt> approvalpaymentreceipts = approvalpaymentreceiptService.findDepartmentData(params);
        List<NkPrepaidpaymentreceipt> prepaidpaymentreceipts = prepaidpaymentreceiptService.findDepartmentData(params);
        if (CollectionUtils.isNotEmpty(directpaymentreceipts)) {
            directpaymentreceipts.forEach(t -> {
                NKReceiptDto temp = new NKReceiptDto();
                temp.setIdorganization(t.getIdorganization());
                temp.setNameorganization(t.getNameorganization());
                temp.setIddepartment(t.getIddepartment());
                temp.setNamedepartment(t.getNamedepartment());
                temp.setYearly(t.getYearly());
                temp.setPaymentperson(t.getPaymentperson());
                temp.setAmount(t.getAmount());
                receiptDtos.add(temp);
            });
        }
        if (CollectionUtils.isNotEmpty(contractpaymentreceipts)) {
            contractpaymentreceipts.forEach(t -> {
                NKReceiptDto temp = new NKReceiptDto();
                temp.setIdorganization(t.getIdorganization());
                temp.setNameorganization(t.getNameorganization());
                temp.setIddepartment(t.getIddepartment());
                temp.setNamedepartment(t.getNamedepartment());
                temp.setYearly(t.getYearly());
                temp.setPaymentperson(t.getPaymentperson());
                temp.setAmount(t.getAmount());
                receiptDtos.add(temp);
            });
        }
        if (CollectionUtils.isNotEmpty(approvalpaymentreceipts)) {
            approvalpaymentreceipts.forEach(t -> {
                NKReceiptDto temp = new NKReceiptDto();
                temp.setIdorganization(t.getIdorganization());
                temp.setNameorganization(t.getNameorganization());
                temp.setIddepartment(t.getIddepartment());
                temp.setNamedepartment(t.getNamedepartment());
                temp.setYearly(t.getYearly());
                temp.setPaymentperson(t.getPaymentperson());
                temp.setAmount(t.getAmount());
                receiptDtos.add(temp);
            });
        }
        if (CollectionUtils.isNotEmpty(prepaidpaymentreceipts)) {
            prepaidpaymentreceipts.forEach(t -> {
                NKReceiptDto temp = new NKReceiptDto();
                temp.setIdorganization(t.getIdorganization());
                temp.setNameorganization(t.getNameorganization());
                temp.setIddepartment(t.getIddepartment());
                temp.setNamedepartment(t.getNamedepartment());
                temp.setYearly(t.getYearly());
                temp.setPaymentperson(t.getPaymentperson());
                temp.setAmount(t.getAmount());
                receiptDtos.add(temp);
            });
        }
        //部门统计
        HashMap<String, List<NKReceiptDto>> collect = receiptDtos.stream().collect(Collectors.groupingBy(t -> t.getIdorganization() + "_" + t.getNameorganization() + "_" + t.getYearly() + "_" + t.getNamedepartment(), HashMap::new, Collectors.toList()));

        Map<String, Integer> _kjndCount = new HashMap<>();
        for (Map.Entry<String, List<NKReceiptDto>> entry : collect.entrySet()) {
            String key = entry.getKey();
            if (key.split("_").length < 4) {
                continue;
            }
            String companyId = key.split("_")[0];
            String companyName = key.split("_")[1];
            Integer kjnd = Integer.parseInt(key.split("_")[2]);
            String departmentName = key.split("_")[3];

            boolean existsBm = false;
            for (String noBm : noBmNames) {
                if (departmentName.contains(noBm)) {
                    existsBm = true;
                    break;
                }
            }
            if (existsBm) {
                continue;
            }
//           if (_kjndCount.containsKey(companyName + "_" + kjnd) && _kjndCount.get(companyName + "_" + kjnd) >= TOPN) {
//                continue;
//            }

            AuditModelStep1 step1 = new AuditModelStep1();
            step1.setCompanyName(companyName);
            step1.setDepartmentName(departmentName);
            step1.setCompanyId(entry.getValue().get(0).getIdorganization());
            step1.setDepartmentId(entry.getValue().get(0).getIddepartment());
            step1.setDepartmentName(entry.getValue().get(0).getNamedepartment());
            step1.setKjnd(kjnd);
            step1.setAmount(entry.getValue().stream().map(NKReceiptDto::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add));
            // TODO 临时取数  personService.countByDepartment(step1.getCompanyId(), step1.getDepartmentId(), step1.getDepartmentName())

            Map<String, Object> p = new HashMap();
            p.put("companyId", companyId);
            p.put("name", departmentName);
            List<AaDepartment> departmentList = departmentService.findAll(p);

            Integer userCount = 0;
            if (CollectionUtils.isNotEmpty(departmentList)) {
                userCount = departmentList.get(0).getPersonCount() == null ? 0 : departmentList.get(0).getPersonCount();
            }
            step1.setPersonCount(userCount);
            if (step1.getPersonCount() == null || step1.getPersonCount() == 0) {
                step1.setAverage(step1.getAmount());
            } else {
                step1.setAverage(step1.getAmount().divide(BigDecimal.valueOf(step1.getPersonCount()), BigDecimal.ROUND_HALF_UP).setScale(2, BigDecimal.ROUND_HALF_UP));
            }
            step1List.add(step1);
            _kjndCount.put(companyName + "_" + kjnd, (_kjndCount.get(companyName + "_" + kjnd) == null ? 0 : _kjndCount.get(companyName + "_" + kjnd)) + 1);
        }
        return step1List;
    }

    //报销费用数据
    private List<AuditModelStep2> packetMaterialData(Map params) {
        if (params.containsKey("topN")) {
            TOPN = (Integer.parseInt(params.get("topN").toString()));
        }
        List<AuditModelStep2> step2List = new ArrayList<>();
        List<NKReceiptDto> receiptDtos = new ArrayList<>();
        List<NkMaterialpurchasereceipt> materialpurchasereceipts = materialpurchasereceiptService.findDepartmentData(params);
        if (CollectionUtils.isNotEmpty(materialpurchasereceipts)) {
            materialpurchasereceipts.forEach(t -> {
                NKReceiptDto temp = new NKReceiptDto();
                temp.setIdorganization(t.getIdorganization());
                temp.setNameorganization(t.getNameorganization());
                temp.setIddepartment(t.getIddepartment());
                temp.setNamedepartment(t.getNamedepartment());
                temp.setYearly(t.getYearly());
                temp.setAmount(t.getAmount());
                receiptDtos.add(temp);
            });
        }

        //部门统计
        HashMap<String, List<NKReceiptDto>> collect = receiptDtos.stream().collect(Collectors.groupingBy(t -> t.getIdorganization() + "_" + t.getNameorganization() + "_" + t.getYearly() + "_" + t.getNamedepartment(), HashMap::new, Collectors.toList()));

        Map<String, Integer> _kjndCount = new HashMap<>();
        for (Map.Entry<String, List<NKReceiptDto>> entry : collect.entrySet()) {
            String key = entry.getKey();
            if (key.split("_").length < 3) {
                continue;
            }

            String companyId = key.split("_")[0];
            String companyName = key.split("_")[1];
            Integer kjnd = Integer.parseInt(key.split("_")[2]);
            String departmentName = key.split("_")[3];

//           if (_kjndCount.containsKey(companyName + "_" + kjnd) && _kjndCount.get(companyName + "_" + kjnd) >= TOPN) {
//                continue;
//            }

            AuditModelStep2 step2 = new AuditModelStep2();
            step2.setCompanyName(companyName);
            step2.setDepartmentName(departmentName);
            step2.setCompanyId(entry.getValue().get(0).getIdorganization());
            step2.setDepartmentId(entry.getValue().get(0).getIddepartment());
            step2.setDepartmentName(entry.getValue().get(0).getNamedepartment());
            step2.setKjnd(kjnd);
            step2.setAmount(entry.getValue().stream().map(NKReceiptDto::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add));

            // TODO 临时取数  personService.countByDepartment(step2.getCompanyId(), step2.getDepartmentId(), step2.getDepartmentName())

            Map<String, Object> p = new HashMap();
            p.put("companyId", companyId);
            p.put("name", departmentName);
            List<AaDepartment> departmentList = departmentService.findAll(p);

            Integer userCount = 0;
            if (CollectionUtils.isNotEmpty(departmentList)) {
                userCount = departmentList.get(0).getPersonCount() == null ? 0 : departmentList.get(0).getPersonCount();
            }
            step2.setPersonCount(userCount);

            if (step2.getPersonCount() == null || step2.getPersonCount() == 0) {
                step2.setAverage(step2.getAmount());
            } else {
                step2.setAverage(step2.getAmount().divide(BigDecimal.valueOf(step2.getPersonCount()), BigDecimal.ROUND_HALF_UP).setScale(2, BigDecimal.ROUND_HALF_UP));
            }
            step2List.add(step2);
            _kjndCount.put(companyName + "_" + kjnd, (_kjndCount.get(companyName + "_" + kjnd) == null ? 0 : _kjndCount.get(companyName + "_" + kjnd)) + 1);
        }
        return step2List;
    }

    @Override
    protected int persistence(AuditModelBaseResult data) {
        int result = 0;

        String content = JSON.toJSONString(data, SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue,
                SerializerFeature.WriteDateUseDateFormat);

        data.setResultFileMd5(Md5Utils.hash(content));
        data.setResultFileUrl("/upload/analyse/model34_" + data.getCompanyId() + "_" + data.getResultFileMd5() + ".json");

        Writer write = null;
        try {
            File file = new File(fileuploadPath + "/upload/analyse/model34_" + data.getCompanyId() + "_" + data.getResultFileMd5() + ".json");
            if (file.exists()) {
                file.delete();
            }
            file.createNewFile();
            // 写入文件
            write = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8);
            write.write(content);
            write.flush();
            write.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                write.close();
            } catch (IOException e) {
                log.error(e.getMessage());
            }
        }
        return result;
    }

    @Override
    public T getData(String modelId, Map param) {
        return analyseResultData(modelId, param);
    }


    /**
     * 从本地加载数据
     */
    public AuditModelBaseResult getDataFromJson(String jsonUrl, Map param) {
        AuditModelResult result = null;
        try {
            String json = FileUtil.readTxtToJson(fileuploadPath + jsonUrl);
            result = JSONObject.parseObject(json, new TypeReference<AuditModelResult>() {
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
        filterCondition(result, param);

        return result;
    }

    /**
     * 本地json数据条件过滤
     */
    private AuditModelResult filterCondition(AuditModelResult orig, Map param) {
        AuditModelResult result = orig;
        String companyId = MapUtils.getString(param, "companyId");
        Integer kjnd = MapUtils.getInteger(param, "kjnd");
        String departmentIds = MapUtils.getString(param, "departmentIds");

        List<AuditModelStep1> list1 = orig.getStep1List();
        List<AuditModelStep2> list2 = orig.getStep2List();
        if (StringUtil.isNotEmpty(companyId)) {
            list1 = list1.stream().filter(t -> t.getCompanyId().equals(companyId)).collect(Collectors.toList());
            list2 = list2.stream().filter(t -> t.getCompanyId().equals(companyId)).collect(Collectors.toList());
        }
        if (kjnd != null) {
            list1 = list1.stream().filter(t -> t.getKjnd().equals(kjnd)).collect(Collectors.toList());
            list2 = list2.stream().filter(t -> t.getKjnd().equals(kjnd)).collect(Collectors.toList());
        }
        if (StringUtil.isNotEmpty(departmentIds)) {
            list1 = list1.stream().filter(t -> departmentIds.contains(t.getDepartmentId())).collect(Collectors.toList());
            list2 = list2.stream().filter(t -> departmentIds.contains(t.getDepartmentId())).collect(Collectors.toList());
        }

        result.setStep1List(list1);
        result.setStep2List(list2);

        return result;
    }


    public void exportExcel(String modelId, Map params, HttpServletResponse response) {
        AuditModelResult t = (AuditModelResult) analyseResultData(modelId, params);

        export(t, response);
    }

    @Override
    public void exportExcelFromJson(String modelId, String jsonUrl, HttpServletResponse response) {
        AuditModelResult t = null;
        try {
            String json = FileUtil.readTxtToJson(fileuploadPath + jsonUrl);
            t = JSONObject.parseObject(json, new TypeReference<AuditModelResult>() {
            });
        } catch (Exception e) {
            e.printStackTrace();
        }

        export(t, response);
    }

    private void export(AuditModelResult t, HttpServletResponse response) {
        List<ExcelData> result = t.getStep1List().stream().map(item -> {
            ExcelData model = new ExcelData();
            BeanUtils.copyProperties(item, model);
            return model;
        }).collect(Collectors.toList());

        response.setContentType("application/vnd.ms-excel");
        response.setHeader("Content-disposition", "attachment;filename=download_1.xls");
        OutputStream ouputStream = null;
        Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(),
                ExcelData.class, result);
        try {
            ouputStream = response.getOutputStream();
            workbook.write(ouputStream);
        } catch (Exception e) {
            throw new RuntimeException("系统异常");
        } finally {
            try {
                if (ouputStream != null) {
                    ouputStream.flush();
                    ouputStream.close();
                }
            } catch (Exception e) {
                throw new RuntimeException("系统异常");
            }
        }
    }
}
